﻿var BACKSPACE_KEY = 8;
var ENTER_KEY = 13;
var ARROW_LEFT_KEY = 37;
var ARROW_UP_KEY = 38;
var ARROW_RIGHT_KEY = 39;
var ARROW_DOWN_KEY = 40;
var DELETE_KEY = 46;

define("ow-epi-tagsProperty/TagsViewModel", [
// dojo
        "dojo/_base/array",
        "dojo/_base/Deferred",
        "dojo/dom-attr",
// dijit
        "dijit/form/ToggleButton",
        "ow-epi-tagsProperty/knockout-3.0.0",
        "epi/shell/widget/SearchBox"
],
    function (array, Deferred, domAttr, ToggleButton, ko) {

        var TagViewModel = function (tag, tagsViewModel) {
            var self = this;

            /*this.cleanUp = function () {
                delete tag;
                delete tagsViewModel;
                delete self.id;
                delete self.parentId;
                delete self.name;

                delete self.isCurrent;
                delete self.isSelected;
                delete self.isSelectable;
                delete self.isExpanded;

                this.test.dispose();
                delete test;
                this.test2.dispose();
                delete test2;

                delete self.children;
                delete self.hasChildrenToShow;

                delete self.descendants;
                delete self.ancestors;

                delete self.isEnabled;

                delete self.isVisible;
                delete self.isChildTag;
                delete self.isMainTag;
                delete self.getParent;
                delete self.getPreviousSibling;
                delete self.getLastChlid;
                delete self.getLastDescendant;
                delete self.getPreviousTreeElement;
                delete self.getPreviousEnabledTreeElement;
                delete self.getNextSibling;
                delete self.getNextAncestorsSibling;
                delete self.getNextTreeElement;
                delete self.getNextEnabledTreeElement;
                delete self.getDescendants;
                delete self.getAncestors;
                delete self.getBreadcrumbs;

                clearTimeout(self.timer);
                delete self.clicks;
                delete self.delay;
                delete self.timer;

                delete self.onTreeElementClick;
                delete self.onToogleExpanded;
            };*/

            this.cleanUp = function() {
                this.isCurrent = ko.observable();
                this.isSelected = ko.observable();
                this.isExpanded = ko.observable();
                this.children = ko.observableArray();
                this.hasChildrenToShow = ko.computed(function() {
                });
                this.descendants = ko.observableArray();
                this.ancestors = ko.observableArray();

                this.isEnabled = ko.computed(function() {
                });
                this.isVisible = ko.computed(function() {
                });
                this.isChildTag = ko.computed(function () {
                });

                this.isMainTag = ko.computed(function () {
                }
                );
            };

            this.tagsViewModel = tagsViewModel;
            this.id = tag.id;
            this.parentId = tag.parentID;
            this.name = ko.observable(tag.name);

            this.isCurrent = ko.observable(false);
            this.isSelected = ko.observable(false);
            this.isSelectable = tag.isSelectable;
            this.isExpanded = ko.observable(false);

            this.test = this.isSelected.subscribe(function (newValue) {
                ko.utils.arrayForEach(self.ancestors(), function (ancestor) {
                    ancestor.isExpanded(true);
                });

                if (newValue && !self.isCurrent()) {
                    tagsViewModel.selectedTreeElement(self);
                }
                if (!newValue && !self.isCurrent()) {
                    tagsViewModel.selectedTreeElement("");
                }
            });

            this.getParent = function () {
                return self.tagsViewModel.allTagsWithIds[self.parentId];
            };

            this.children = ko.observableArray();

            this.hasChildrenToShow = ko.computed(function () {
                if (self.children().length == 0)
                    return false;

                var firstVisibleOrEnabledChild = ko.utils.arrayFirst(self.children(), function (item) {
                    if (item.isVisible() || item.isEnabled())
                        return true;

                    var enabledDescendant = ko.utils.arrayFirst(item.descendants(), function (d) {
                        return d.isEnabled();
                    });

                    if (enabledDescendant)
                        return true;

                    return false;
                });

                if (firstVisibleOrEnabledChild)
                    return true;

                return false;
            });

            this.descendants = ko.observableArray();
            this.ancestors = ko.observableArray();

            this.isEnabled = ko.computed(function () {
                return (self.isSelectable && !self.isCurrent() && (tagsViewModel.nameSearch().length == 0 || ko.utils.stringStartsWith(self.name().toLowerCase(), tagsViewModel.nameSearch().toLowerCase())));
            }).extend({ throttle: 500 });

            this.test2 = this.isEnabled.subscribe(function (newValue) {
                if (newValue && tagsViewModel.nameSearch().length > 0) {
                    ko.utils.arrayForEach(self.ancestors(), function (ancestor) {
                        ancestor.isExpanded(true);
                    });
                }

                if (tagsViewModel.selectFirstEnabledTreeEelement && ((!newValue && self.isSelected()) || !tagsViewModel.selectedTreeElement()))
                    tagsViewModel.selectFirstEnabledTreeEelement();
            });

            this.isVisible = ko.computed(function () {
                var collapsedAncestor = ko.utils.arrayFirst(self.ancestors(), function (d) {
                    return !d.isExpanded();
                });

                if (collapsedAncestor)
                    return false;

                if (self.isEnabled())
                    return true;

                var enabledDescendant = ko.utils.arrayFirst(self.descendants(), function (d) {
                    return d.isEnabled();
                });

                if (enabledDescendant)
                    return true;

                return false;
            });

            this.isChildTag = ko.computed(function () {
                return (self.parentId && self.parentId !== 0);
            });

            this.isMainTag = ko.computed(function () {
                return !self.isChildTag();
            }
            );

            this.getPreviousSibling = function () {
                var siblings;

                if (self.isMainTag()) {
                    siblings = ko.utils.arrayFilter(tagsViewModel.allTags(), function (t) {
                        return t.isMainTag();
                    });
                }
                else
                    siblings = self.getParent().children();

                var selfIndex = siblings.indexOf(self);

                if (selfIndex === 0)
                    return undefined;

                return siblings[selfIndex - 1];
            };

            this.getLastChlid = function () {
                var children = self.children();

                if (children.lenght == 0)
                    return null;

                return children[children.length - 1];
            };

            this.getLastDescendant = function () {
                var lastDescendant;
                var current = lastDescendant = self.getLastChlid();

                while (current) {
                    current = current.getLastChlid();
                    if (current)
                        lastDescendant = current;
                }


                return lastDescendant;
            };

            this.getPreviousTreeElement = function () {
                var previousSibling = self.getPreviousSibling();

                if (previousSibling) {
                    var previousSiblingLastDescendant = previousSibling.getLastDescendant();
                    if (previousSiblingLastDescendant)
                        return previousSiblingLastDescendant;
                }

                if (previousSibling)
                    return previousSibling;

                if (self.isMainTag())
                    return null;

                return self.getParent();
            };


            this.getPreviousEnabledTreeElement = function () {
                var n = self.getPreviousTreeElement();
                if (!n)
                    return null;

                if (n.isEnabled() && n.isVisible() && !n.isCurrent())
                    return n;

                return n.getPreviousEnabledTreeElement();
            };

            this.getNextSibling = function () {
                var siblings;

                if (self.isMainTag()) {
                    siblings = ko.utils.arrayFilter(tagsViewModel.allTags(), function (t) {
                        return t.isMainTag();
                    });
                }
                else
                    siblings = self.getParent().children();

                var selfIndex = siblings.indexOf(self);

                return siblings[selfIndex + 1];
            };

            this.getNextAncestorsSibling = function () {
                if (self.isMainTag())
                    return null;

                var ancestor = self.getParent();

                var sibling = ancestor.getNextSibling();
                if (sibling)
                    return sibling;

                if (ancestor.isMainTag())
                    return null;

                return ancestor.getNextAncestorsSibling();
            };

            this.getNextTreeElement = function () {
                if (self.children()[0] !== undefined)
                    return self.children()[0];

                var nextSibling = self.getNextSibling();
                if (nextSibling)
                    return nextSibling;

                if (self.isMainTag())
                    return null;

                return self.getNextAncestorsSibling();
            };


            this.getNextEnabledTreeElement = function () {
                var n = self.getNextTreeElement();
                if (!n)
                    return null;

                if (n.isEnabled() && !n.isCurrent())
                    return n;

                return n.getNextEnabledTreeElement();
            };


            this.getDescendants = function () {
                var descendants = new Array();

                descendants = descendants.concat(self.children());

                ko.utils.arrayForEach(self.children(), function (child) {
                    descendants = descendants.concat(child.getDescendants());
                });

                return descendants;
            };

            this.getAncestors = function () {
                var ancestors = new Array();

                if (self.getParent()) {
                    ancestors.push(self.getParent());
                    ancestors = ancestors.concat(self.getParent().getAncestors());
                }

                return ancestors;
            };


            this.getBreadcrumbs = function () {
                if (!self.getParent())
                    return this.name();

                return self.getParent().getBreadcrumbs() + " \\ " + this.name();

            };

            self.clicks = 0;
            self.delay = 1000;
            self.timer = null;

            this.onTreeElementClick = function (viewModel) {
                clearTimeout(self.timer);
                self.timer = setTimeout(function () {
                    self.clicks = 0;
                    clearTimeout(self.timer);
                }, self.delay);

                self.clicks++;

                tagsViewModel.isSearchboxSelected(true);

                if (!self.isEnabled())
                    return false;

                tagsViewModel.deselectedSelectedElement();
                viewModel.isSelected(true);


                if (self.clicks == 2) {
                    self.tagsViewModel.addSelectedToCurrent();
                    self.clicks = 0;
                    clearTimeout(self.timer);
                }

                return false;
            };

            this.onToogleExpanded = function () {
                tagsViewModel.isSearchboxSelected(true);

                if (self.isExpanded())
                    self.isExpanded(false);
                else
                    self.isExpanded(true);
            };
        };

        var TagsViewModel = function (widget, focusUtil, current, all) {
            var self = this;

            this.nameSearch = ko.observable('');

            /*this.cleanUp = function () {
                self.allTags.each(function () {
                    this.cleanUp();
                    delete this;
                });

                delete self.allTags;
                
                this.test.dispose();
                delete test;
                delete self.isSearchboxSelected;
                
                delete self.selectedTreeElement;

                delete self.allMainTags;
                delete self.currentTags;
                delete self.deselectedSelectedElement;
                delete self.selectFirstEnabledTreeEelement;
                delete self.save;
                delete self.onSearchboxKey;
                delete self.addSelectedToCurrent;
                delete self.navigateThroughTagsTree;
                delete self.navigateToCurrentTagsList;
                delete self.onCurrentTagKey;
                delete self.removeTagFromCurrent;
                delete self.navigateThroughCurrentTagsList;
                delete self.onCurrentTagRemoveClick;
                delete self.scrollTree;
                delete self.allTagsWithIds;
                delete self.allTagsByParents;
            };*/

            this.cleanUp = function() {
                self.allTags.each(function() {
                    this.cleanUp();
                    delete this;
                });
                
                this.isSearchboxSelected = ko.observable();
                this.selectedTreeElement = ko.observable();
                this.allTags = ko.observableArray();
                this.allMainTags = ko.computed(function() {
                });
                this.currentTags = ko.computed(function() {
                });
            };

            this.isSearchboxSelected = ko.observable(false);
            this.test = this.isSearchboxSelected.subscribe(function (newValue) {
                if (!newValue)
                    return;

                var selected = self.selectedTreeElement();
                if (selected && selected.isEnabled())
                    return;

                self.selectFirstEnabledTreeEelement();
            });

            this.selectedTreeElement = ko.observable();

            this.allTags = ko.observableArray(
                ko.utils.arrayMap(all, function (t) {
                    var tag = new TagViewModel(t, self);

                    var correspondingInCurrent = ko.utils.arrayFirst(current, function (item) {
                        if (!item)
                            return false;
                        return item.id == t.id;
                    });

                    if (correspondingInCurrent)
                        tag.isCurrent(true);

                    return tag;
                })
            );

            this.allMainTags = ko.computed(function () {
                return ko.utils.arrayFilter(self.allTags(), function (t) {
                    return t.isMainTag();
                });
            });

            this.currentTags = ko.computed(function () {
                return ko.utils.arrayFilter(self.allTags(), function (t) {
                    return t.isCurrent();
                });
            });

            this.deselectedSelectedElement = function () {
                var selected = self.selectedTreeElement();
                if (selected)
                    selected.isSelected(false);
            };

            this.selectFirstEnabledTreeEelement = function () {
                self.deselectedSelectedElement();

                var firstTreeElement = self.allMainTags()[0];
                if (!firstTreeElement)
                    return;

                if (firstTreeElement.isEnabled() && !firstTreeElement.isCurrent()) {
                    firstTreeElement.isSelected(true);
                    return;
                }

                var firstEnabled = firstTreeElement.getNextEnabledTreeElement();
                if (firstEnabled)
                    firstEnabled.isSelected(true);
            };

            this.save = function () {
                /*widget.onFocus();
                setTimeout(function () {*/
                var ids = ko.utils.arrayMap(self.currentTags(), function (t) {
                    return t.id;
                });
                widget._set("value", ids);
                widget.onChange(ids);
                /*focusUtil.curNode.blur();
                focusUtil.focus(widget.searchbox);
            }, 0);*/
            },

            this.onSearchboxKey = function (viewModel, event) {
                switch (event.keyCode) {
                    case ENTER_KEY:
                    case ARROW_UP_KEY:
                    case ARROW_DOWN_KEY:
                        return viewModel.navigateThroughTagsTree(viewModel, event);
                        break;
                    case BACKSPACE_KEY:
                    case ARROW_LEFT_KEY:
                        return viewModel.navigateToCurrentTagsList(viewModel, event);
                        break;
                }

                return true;
            };

            this.addSelectedToCurrent = function () {
                var selected = self.selectedTreeElement();
                if (!selected)
                    return;
                selected.isSelected(false);
                selected.isCurrent(true);

                self.selectFirstEnabledTreeEelement();

                self.nameSearch("");
                self.save();
            };

            this.navigateThroughTagsTree = function (viewModel, event) {
                var selected = self.selectedTreeElement();


                this.trySelectNextTreeElement = function () {
                    if (!selected)
                        return;

                    var nextEnabledTreeElement = selected.getNextEnabledTreeElement();
                    if (nextEnabledTreeElement) {
                        selected.isSelected(false);
                        nextEnabledTreeElement.isSelected(true);
                    }
                };

                this.trySelectPreviousTreeElement = function () {
                    if (!selected)
                        return;

                    var previousEnabledTreeElement = selected.getPreviousEnabledTreeElement();
                    if (previousEnabledTreeElement) {
                        selected.isSelected(false);
                        previousEnabledTreeElement.isSelected(true);
                    }
                };

                switch (event.keyCode) {
                    case ENTER_KEY:
                        self.addSelectedToCurrent();
                        self.scrollTree();
                        break;
                    case ARROW_UP_KEY:
                        this.trySelectPreviousTreeElement();
                        self.scrollTree();
                        break;
                    case ARROW_DOWN_KEY:
                        this.trySelectNextTreeElement();
                        self.scrollTree();
                        break;
                }

                return false;
            };

            this.navigateToCurrentTagsList = function (viewModel, event) {
                var currentTags = viewModel.currentTags();

                if (widget.getCaretPosition() != 0) {
                    return true;
                }

                if (window.getSelection && window.getSelection().type != "Caret") {
                    return true;
                }

                if (currentTags.length < 1) {
                    return true;
                }

                currentTags[currentTags.length - 1].isSelected(true);

                return false;
            };

            this.onCurrentTagKey = function (viewModel, event) {
                switch (event.keyCode) {
                    case BACKSPACE_KEY:
                    case ARROW_LEFT_KEY:
                        return viewModel.navigateThroughCurrentTagsList(viewModel, event);
                        break;
                }

                return true;
            };

            this.removeTagFromCurrent = function (tag) {
                tag.isSelected(false);
                tag.isCurrent(false);
                self.save();
            };

            this.navigateThroughCurrentTagsList = function (viewModel, event) {
                this.selectAtIndex = function (i) {
                    if (self.currentTags().length <= i || self.currentTags().length == 0) {
                        self.isSearchboxSelected(true);
                        return;
                    }

                    if (i < 0) {
                        self.currentTags()[0].isSelected(true);
                        return;
                    }

                    viewModel.isSelected(false);
                    self.currentTags()[i].isSelected(true);
                };

                this.removeSelectedFromCurrent = function () {
                    viewModel.isSelected(false);

                    viewModel.isCurrent(false);
                    self.save();
                };

                var index = self.currentTags().indexOf(viewModel);

                switch (event.keyCode) {
                    case ARROW_LEFT_KEY:
                        this.selectAtIndex(index - 1);
                        break;
                    case ARROW_RIGHT_KEY:
                        this.selectAtIndex(index + 1);
                        break;
                    case BACKSPACE_KEY:
                        self.removeTagFromCurrent(viewModel);
                        this.selectAtIndex(index - 1);
                        break;
                    case DELETE_KEY:
                        self.removeTagFromCurrent(viewModel);
                        this.selectAtIndex(index);
                        break;
                    default:
                        self.isSearchboxSelected(true);
                        return true;
                }

                return false;
            };



            this.onCurrentTagRemoveClick = function (viewModel, event) {
                self.removeTagFromCurrent(viewModel);
                self.isSearchboxSelected(true);
            };

            this.scrollTree = function () {
                var tree = $("#" + widget.id + " .tree");
                var node = $("#" + widget.id + " .tree-node-selected > div");

                if (tree.length === 0 || node.length === 0) {
                    return;
                }

                var currentScroll = tree.scrollTop();
                var differenceToScroll;
                var treeTopPosition = tree.offset().top;
                var nodeTopPosition = node.offset().top;

                differenceToScroll = nodeTopPosition - treeTopPosition;
                if (differenceToScroll < 0) {
                    tree.scrollTop(currentScroll + differenceToScroll);
                    return;
                }

                var treeBottomPosition = tree.offset().top + tree.innerHeight();
                var nodeBottomPosition = node.offset().top + node.outerHeight();
                differenceToScroll = nodeBottomPosition - treeBottomPosition;

                if (differenceToScroll > 0) {
                    tree.scrollTop(currentScroll + differenceToScroll);
                    return;
                }
            };

            self.allTagsWithIds = [];
            self.allTagsByParents = [];

            ko.utils.arrayForEach(self.allTags(), function (tag) {
                self.allTagsWithIds[tag.id] = tag;

                if (self.allTagsByParents[tag.parentId]) {
                    self.allTagsByParents[tag.parentId].push(tag);
                } else {
                    self.allTagsByParents[tag.parentId] = [tag];
                }
            });

            ko.utils.arrayForEach(self.allTags(), function (tag) {
                if (self.allTagsByParents[tag.id]) {
                    tag.children(self.allTagsByParents[tag.id]);
                }
            });


            ko.utils.arrayForEach(self.allTags(), function (tag) {
                tag.descendants(tag.getDescendants());
                tag.ancestors(tag.getAncestors());
            });
        };

        return TagsViewModel;
    });